﻿/*! ************************************************************
 * \brief 本文提供一个有关YAML数据处理的工具类
 * \authors 林胜勇
 * \verbatim
 * 变更记录：
 * 2024-12-06，林胜勇:
 *   创建初始版本v1.0.0
 * \endverbatim
 ****************************************************************/

#pragma once

#include <algorithm>
#include <exception>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <sstream>
#include <string>

#ifndef uint
#define uint unsigned int
#endif
#ifndef ulong
#define ulong unsigned long
#endif

/*!
 * \example
 * \brief YAML工具类的使用例子
 * \code
   示例如下：

 * \endcode
 */
namespace cppc {
#define YAML_IgnoreEmptyLineDefault false ///< 读取YAML内容时，是否忽略空行和注释行
#define YAML_PrintLinesDefault false      ///< 读取YAML内容时，是否打印加载的行信息，用于调试
#define YAML_QuoteCharNone '\0'           ///< 无引号时的标志
namespace yaml {
class YamlNodeIterator;
class ParseImp;

/// 去除前面的不可见字符
std::string LeftTrim(const std::string &str);

/// 去除后面的不可见字符
std::string RightTrim(const std::string &str);

/// 去除前后的不可见字符，保留中间有效字符串
std::string Trim(const std::string &str);

/// 分割字符串
std::vector<std::string> SplitStr(const std::string &str, const char delimiter = '.');

/// 去除小数点后无效的的0
std::string RemoveTrailingZeros(const std::string &str);

/// 是否可能是数值，整数和浮点数
bool IsValidNumeric(const std::string &str);

/// 创建文件夹并循环创建父文件夹
/// \param mode 参数在Windows上被忽略，在Linux上用于设置目录权限。
///             777意义：文件所有者、所在组、其他用户均为读写执行。二进制：读4、写2、执行1
bool CreateDirectoryx(const std::string &path, int mode = 0777);

/// 提取文件所在目录
std::string GetDirectoryFromFilePath(const std::string &filePath);

/// 将字符串转换为任何数据类型。
template <typename T>
struct StringConverter {
    static T Get(const std::string &data) {
        T type;
        std::stringstream ss(data);
        ss >> type;
        return type;
    }
    static T Get(const std::string &data, const T &defaultValue) {
        std::istringstream ss(data);
        T type;
        if (!(ss >> type)) {
            return defaultValue;
        }
        return type;
    }
    static std::pair<bool, T> GetPair(const std::string &data, const T &defaultValue) {
        T type;
        bool useDefault = false;
        std::istringstream ss(data);
        if (!(ss >> type)) {
            type = defaultValue;
            useDefault = true;
        }
        return {useDefault, type};
    }
};

template <>
struct StringConverter<std::string> {
    static std::string Get(const std::string &data) {
        return data;
    }

    static std::string Get(const std::string &data, const std::string &defaultValue) {
        return data.empty() ? defaultValue : data;
    }

    static std::pair<bool, std::string> GetPair(const std::string &data, const std::string &defaultValue) {
        return {data.empty(), data.empty() ? defaultValue : data};
    }
};
template <>
struct StringConverter<bool> {
    static bool Get(const std::string &data) {
        std::string tmpData = data;
        tmpData = Trim(tmpData);
        std::transform(tmpData.begin(), tmpData.end(), tmpData.begin(), ::tolower);
        if (tmpData == "on" || tmpData == "true" || tmpData == "yes" || tmpData == "1") {
            return true;
        }
        return false;
    }

    static bool Get(const std::string &data, const bool &defaultValue) {
        std::string tmpData = data;
        tmpData = Trim(tmpData);
        if (tmpData.empty()) {
            return defaultValue;
        }
        return Get(data);
    }

    static std::pair<bool, bool> GetPair(const std::string &data, const bool &defaultValue) {
        bool type = defaultValue;
        std::string tmpData = data;
        tmpData = Trim(tmpData);
        if (!tmpData.empty()) {
            std::transform(tmpData.begin(), tmpData.end(), tmpData.begin(), ::tolower);
            if (tmpData == "on" || tmpData == "true" || tmpData == "yes" || tmpData == "1") {
                type = true;
            } else {
                type = false;
            }
        }
        return {tmpData.empty(), type};
    }
};

/// 节点类
class YamlNode
{
    friend class YamlNodeIterator;
    friend class ParseImp;

public:
    /// 节点类型
    enum NodeType {
        None,     ///< 未知/空
        Sequence, ///< 序列
        Map,      ///< 映射
        Scalar,   ///< 元值
        EmptyLine ///< 空行/注释行
    };

    YamlNode();
    YamlNode(YamlNode &node);
    YamlNode(const std::string &value);
    YamlNode(const char *value);
    ~YamlNode();

    /// 完全清除子节点，并将自身类型设置为None
    void clean();

    /// 赋值操作符，深拷贝
    YamlNode &operator=(YamlNode &node);

    /// 赋值操作符，只能向元值对象赋值
    YamlNode &operator=(const std::string &value);

    /// 赋值操作符，只能向元值对象赋值
    YamlNode &operator=(const char *value);

    /// 赋值操作符，只能向元值对象赋值
    YamlNode &operator=(bool value);

    /// 模板赋值操作符，只接受数值类型
    template <typename T, typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
    YamlNode &operator=(const T &value) {
        std::ostringstream oss;
        oss << value;
        *this = RemoveTrailingZeros(oss.str());
        return *this;
    }

    /// 将from的所有子对象拷贝至to中，清除to原有子节点
    static void CopyNode(const YamlNode &from, YamlNode &to);

    /// 将from的所有子对象拷贝追加至to中，不清除原有子节点
    static void AppendToNode(const YamlNode &from, YamlNode &to);

public: // 取值接口
    /// 取值，将节点作为给定的模板类型
    template <typename T>
    T as() const {
        return StringConverter<T>::Get(asString());
    }

    /// 将节点作为给定的模板类型获取
    template <typename T>
    T as(const T &defaultValue) const {
        return StringConverter<T>::Get(asString(), defaultValue);
    }

    /// 将节点作为给定的模板类型获取
    /// \return <是否使用了默认值，获取值>
    template <typename T>
    std::pair<bool, T> asPair(const T &defaultValue) const {
        if (m_nodeType == YamlNode::Scalar) {
            return StringConverter<T>::GetPair(asString(), defaultValue);
        }
        return {true, defaultValue};
    }

    /// 获取字符串，不是元值则返回空
    std::string asString() const;
    /// 获取字符串，不是元值则返回默认值
    std::string asString(const std::string &defaultValue) const;
    /// 获取字符串，不是元值则返回默认值
    /// \return <是否使用了默认值，获取值>
    std::pair<bool, std::string> asStringPair(const std::string &defaultValue) const;

    /// 获取整数，不是元值则返回0
    int asInt() const;
    /// 获取整数，不是元值则返回默认值
    int asInt(int defaultValue) const;
    /// 获取整数，不是元值则返回默认值
    /// \return <是否使用了默认值，获取值>
    std::pair<bool, int> asIntPair(int defaultValue) const;

    long asLong() const;
    long asLong(long defaultValue) const;
    std::pair<bool, long> asLongPair(long defaultValue) const;

    long long asLongLong() const;
    long long asLongLong(long long defaultValue) const;
    std::pair<bool, long long> asLongLongPair(long long defaultValue) const;

    uint asUInt() const;
    uint asUInt(uint defaultValue) const;
    std::pair<bool, uint> asUIntPair(uint defaultValue) const;

    ulong asULong() const;
    ulong asULong(ulong defaultValue) const;
    std::pair<bool, ulong> asULongPair(ulong defaultValue) const;

    double asDouble() const;
    double asDouble(double defaultValue) const;
    std::pair<bool, double> asDoublePair(double defaultValue) const;

    /// 获取bool值
    ///  \return on/true/yes/1返回true，其他false
    bool asBool() const;
    bool asBool(bool defaultValue) const;
    std::pair<bool, bool> asBoolPair(bool defaultValue) const;

public:
    /// 获取节点类型
    NodeType nodeType() const;

    /// 获取所有子节点数量，包括空行/注释行
    size_t allSize() const;

    /// 获取有效子节点数量，不包括空行/注释行
    size_t size() const;

    /// 指向第一项的迭代器
    YamlNodeIterator begin();

    /// 指向最后一项的迭代器
    YamlNodeIterator end();

    /// 指向最后一项的反向迭代器
    YamlNodeIterator rbegin();

    /// 指向第一项的反向迭代器
    YamlNodeIterator rend();

public: // 删除接口
    /// 删除迭代项，map和序列均可用。
    /// \return 返回删除后的下一个迭代项，删除失败/未找到时返回it
    YamlNodeIterator erase(YamlNodeIterator &it);

    /// 删除索引项，map和序列均可用。
    /// \return 是否删除成功，索引超出时返回失败
    void erase(const size_t index);

    /// 删除map项
    void erase(const std::string &key);

    /// 删除指定节点
    /// \warning 删除成功后不允许再使用node引用对象，因为指向对象已被删除
    /// \return 是否删除成功
    bool erase(YamlNode &node);

    /// 将指定位置项目移到最后
    bool moveToEnd(const size_t index);

    /// 将指定迭代项目移到最后
    bool moveToEnd(YamlNodeIterator &it);

public: // 添加接口
    /// 在前面添加新项，可操作序列和map
    YamlNode &pushFront();

    /// 在末尾添加新项，可操作序列和map
    /// 默认插入时紧跟最后一个有效项
    /// \param beforeEmptyLine 是否插入到末尾空行之前，倒着找最后一个非空行，在其后追加
    YamlNode &pushBack(bool pushBeforeEmptyLine = true);

    /// 末尾添加新的空行，可以设置注释内容来转为注释行
    YamlNode &pushEmptyLine();

    /// 在给定索引处插入序列项，可操作序列和map
    /// 如果索引大于序列大小，则在序列末尾添加新项
    YamlNode &insert(size_t index);

    /// 在指定对象前插入一行新节点，可操作序列和map
    YamlNode &insertBefore(YamlNode &node);

    /// 获取序列项，获取有效子节点
    /// \warning 强制将当前节点转为序列
    /// \param index 序列索引
    /// \return 如果索引超出，末尾追加对象并返回
    YamlNode &operator[](const size_t index);

    /// 获取映射项，返回的没有空行
    /// \warning 强制将当前节点转为map
    /// \param key 映射键。
    /// \return 如果key对象不存在，创建一个新节点
    YamlNode &operator[](const std::string &key);

public: // 取子对象接口
    /// 获取map子对象的key列表
    std::vector<std::string> getSubNodeKeyList();

    /// map是否有指定KEY的子节点
    /// \param key 节点key
    /// \param recursiveCheck 是否递归查找，此时将key按逗号.分割层次
    bool hasSubNode(const std::string &key, bool recursiveCheck = false);

    /// map是否有指定KEY的子节点
    /// \param keyList 按层次分割的key列表
    bool hasSubNode(const std::vector<std::string> &keyList);

    /// 获取map指定KEY的有效子节点
    /// \param key 节点key
    /// \param recursiveCheck 是否递归查找，此时将key按逗号.分割层次
    YamlNode &getSubNode(const std::string &key, bool recursiveCheck = false);

    /// 获取map指定KEY的有效子节点
    /// \param keyList 按层次分割的key列表
    YamlNode &getSubNode(const std::vector<std::string> &keyList);

    /// 获取子节点，序列或map均可用
    /// \param index 序列索引
    /// \param onlyValidNode 是否仅查询有效节点，排除空行
    /// \return 如果索引超出，返回 None 类型节点
    YamlNode &getSubNode(const size_t index, bool onlyValidNode = false);

public: // 类型检查
    bool isNone() const;
    bool isEmptyLine() const;
    bool isSequence() const;
    bool isFlowSequence() const;
    bool isMap() const;
    bool isFlowMap() const;
    bool isScalar() const;

    bool isFlow() const;
    void setIsFlow(bool value);

public:
    /// 获取当前节点的key，只有map节点有效
    std::string nodeKey() const;

    /// 设置节点的key，只有map有效
    void setNodeKey(const std::string &value);

    std::string nodeValue() const;
    void setNodeValue(const std::string &nodeValue);

    /// 获取注释
    std::string comment() const;
    /// 设置注释，会根据value是否为空自动调用setHasComment
    void setComment(const std::string &value);
    /// 是否有注释
    bool hasComment() const;
    /// 设置有无注释，注释内容为空时，强制输出一个#
    void setHasComment(bool hasComment);

    /// KEY是否有引号包裹
    bool hasKeyQuoteChar() const;
    /// 获取KEY包裹引号
    char keyQuoteChar() const;
    /// 设置KEY包裹引号
    void setKeyQuoteChar(char quoteChar);

    /// 值是否有引号包裹
    bool hasValueQuoteChar() const;
    /// 获取值包裹引号
    char valueQuoteChar() const;
    /// 设置值包裹引号
    void setValueQuoteChar(char quoteChar);

    bool isBlockScalar() const;
    void setIsBlockScalar(bool isBlockScalar);

    char blockFlags() const;
    void setBlockFlags(char blockFlags);

    void initEmptyLine();
    void initSequence();
    void initMap();
    void initScalar();

private:
    void setNodeType(YamlNode::NodeType type);

    /// 将前面的空白行平移至最后，在处理流式数据时使用
    bool moveFrontEmptyLineToEnd();

    /// 在末尾增加一个子对象，先不强制类型，也不会修改当前节点类型
    YamlNode &addSubNode();

    /// 在最后一个非空行后插入新节点
    void insertNodeAfterNotEmptyLine(YamlNode *pNode);

private:
    std::list<YamlNode *> m_subNodes; ///< 序列和map的子节点

    YamlNode::NodeType m_nodeType; ///< 节点类型
    bool m_isFlow;                 ///< 是否是流式序列或MAP
    bool m_isBlockScalar;          ///< 是否多行字符串
    char m_blockFlags;             ///< 多行字符串的标识

    std::string m_comment;   ///< 注释
    bool m_hasComment;       ///< 是否有注释
    std::string m_nodeKey;   ///< 节点的key，只有map有效
    std::string m_nodeValue; ///< 元值对象的字符串值
    char m_keyQuoteChar;     ///< KEY是否被引号包裹，\0是无引号
    char m_valueQuoteChar;   ///< 元值是否被引号包裹，\0是无引号
};

/// 节点迭代器
class YamlNodeIterator
{
public:
    friend class YamlNode;

    YamlNodeIterator();
    YamlNodeIterator(const YamlNodeIterator &it);
    YamlNodeIterator &operator=(const YamlNodeIterator &it);
    ~YamlNodeIterator();

    ///  获取迭代器的节点
    /// \return map：<key,Node>，序列：<空,Node>。
    std::pair<const std::string &, YamlNode &> operator*();

    YamlNodeIterator &operator++(int);
    YamlNodeIterator &operator--(int);
    bool operator==(const YamlNodeIterator &it);
    bool operator!=(const YamlNodeIterator &it);

    /// 将迭代器设置为首项
    void initBegin(YamlNode *imp);

    /// 将迭代器设置为末项
    void initEnd(YamlNode *imp);

    /// 将迭代器设置为反向首项
    void initRBegin(YamlNode *imp);

    /// 将迭代器设置为反向末项
    void initREnd(YamlNode *imp);

private:
    YamlNode::NodeType m_Type; ///< 迭代器类型
    bool m_isRInterator;
    std::list<YamlNode *>::iterator m_Iterator;          ///< 迭代器对象
    std::list<YamlNode *>::reverse_iterator m_RIterator; ///< 迭代器对象
};

/// 解析yaml内容
/// \param root       要填充的根节点
/// \param filename   输入文件的路径
/// \throw Exception  发生错误
void ParseFile(YamlNode &root, const std::string &filePath, bool ignoreEmptyLine = YAML_IgnoreEmptyLineDefault, bool printLines = YAML_PrintLinesDefault);

/// 解析yaml内容
/// \param root       要填充的根节点
/// \param stream     输入流
/// \throw Exception  发生错误
void Parse(YamlNode &root, std::iostream &stream, bool ignoreEmptyLine = YAML_IgnoreEmptyLineDefault, bool printLines = YAML_PrintLinesDefault);

/// 解析yaml内容
/// \param root       要填充的根节点
/// \param string     输入数据的字符串
/// \throw Exception  发生错误
void Parse(YamlNode &root, const std::string &string, bool ignoreEmptyLine = YAML_IgnoreEmptyLineDefault, bool printLines = YAML_PrintLinesDefault);

/// 解析yaml内容
/// \param root       要填充的根节点
/// \param buffer     输入数据的字符数组
/// \param size       缓冲区大小
/// \throw Exception  发生错误
void Parse(YamlNode &root, const char *buffer, const size_t size, bool ignoreEmptyLine = YAML_IgnoreEmptyLineDefault, bool printLines = YAML_PrintLinesDefault);

/// 序列化时配置参数，描述输出行为
struct YamlSerializeConfig {
    YamlSerializeConfig(const size_t spaceIndentation = 2, const size_t scalarMaxLength = 0, const bool sequenceMapNewline = false, const bool mapScalarNewline = false);

    size_t SpaceIndentation; ///< 节点层级间缩进的空格数，需大于等于2
    size_t ScalarMaxLength;  ///< 标量的最大长度。如果超出，则序列化为折叠标量
    bool SequenceMapNewline; ///< 如果父节点是序列，则将映射放在新行
    bool MapScalarNewline;   ///< 如果父节点是映射，则将标量放在新行
};

/// 序列化函数
/// \param root       要序列化的根节点
/// \param filePath   输出文件的路径
/// \param config     序列化配置
void SerializeToFile(YamlNode &root, const std::string &filePath, const YamlSerializeConfig &config = {2, 128, false, false});

/// 序列化函数
/// \param root       要序列化的根节点
/// \param stream     输出流
/// \param config     序列化配置
void Serialize(YamlNode &root, std::iostream &stream, const YamlSerializeConfig &config = {2, 128, false, false});

/// 序列化函数
/// \param root       要序列化的根节点
/// \param string     输出数据的字符串
/// \param config     序列化配置
void Serialize(YamlNode &root, std::string &string, const YamlSerializeConfig &config = {2, 128, false, false});
} // namespace yaml
} // namespace cppc
